<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<html>
<head>
<title>Ebtables Hacking HOWTO: Examples</title>
</head>
<body>
<a HREF="ebtables-hacking-HOWTO-3.html">Previous</a>
<a HREF="ebtables-hacking-HOWTO.html#toc4">Contents</a>
<hr>
<h2><a NAME="s4">4.</a> <a HREF="netfilter-hacking-HOWTO.html#toc4">Example</a></h2>
<p>
This section contains annotated example source code. This is just to give some clues, see the full code of the already implemented ebtables modules
for more clues.
</p>
<h2><a NAME="ss4.1">4.1</a> <a HREF="ebtables-hacking-HOWTO.html#toc4.1">Userspace</a></h2></p>
<p>
<em><h3>The userspace ip match module</h3></em>
</p>
<p>
What follows is annotated code of pieces of the <CODE>ebt_ip.c</CODE> code. For brevity, only the <CODE>--ip-source</CODE> option is considered.
</p>
<p>
<table BGCOLOR="#E0E0E0" WIDTH="100%">
<tr><td>
<PRE>
#include &lt;stdio.h&gt;
#include &lt;getopt.h&gt;
#include "../include/ebtables_u.h"
#include &lt;linux/netfilter_bridge/ebt_ip.h&gt;
</PRE>
</td></tr>
</table>
</p>
<p>
At least these includes are needed: respectively for <CODE>printf()</CODE>, for the parsing of the options, for all the
general ebtables userspace definitions and for the ebtables ip module specific header.
</p>
<p>
<table BGCOLOR="#E0E0E0" WIDTH="100%">
<tr><td>
<PRE>
#define IP_SOURCE '1'
#define IP_DEST   '2'

static struct option opts[] =
{
	{ "ip-source"     , required_argument, 0, IP_SOURCE },
	{ "ip-src"        , required_argument, 0, IP_SOURCE },
	{ 0 }
};
</PRE>
</td></tr>
</table>
</p>
<p>
Defining the new command line options. This is a struct that the library function <CODE>getopt_long()</CODE>
understands. The first field specifies the full length option name, the second field specifies if an argument is required
or not, the third field should be zero and the last field specifies the value returned by <CODE>getopt_long()</CODE> when
it encounters this option.
</p>
<p>
<table BGCOLOR="#E0E0E0" WIDTH="100%">
<tr><td>
<PRE>
static void print_help()
{
	printf(
"ip options:\n"
"--ip-src    [!] address[/mask]: ip source specification\n"
}
</PRE>
</td></tr>
</table>
</p>
<p>
Short and descriptive help.
</p>
<p>
<table BGCOLOR="#E0E0E0" WIDTH="100%">
<tr><td>
<PRE>
static void init(struct ebt_entry_match *match)
{
	struct ebt_ip_info *ipinfo = (struct ebt_ip_info *)match->data;

	ipinfo->invflags = 0;
	ipinfo->bitmask = 0;
}
</PRE>
</td></tr>
</table>
</p>
<p>
Initialize the module specific data. In the example, some fields in the struct specific to the ip match module are given an initial value.
</p>
<p>
<table BGCOLOR="#E0E0E0" WIDTH="100%">
<tr><td>
<PRE>
#define OPT_SOURCE 0x01
static int parse(int c, char **argv, int argc, const struct ebt_u_entry *entry,
   unsigned int *flags, struct ebt_entry_match **match)
{
	struct ebt_ip_info *ipinfo = (struct ebt_ip_info *)(*match)->data;
	char *end;
	long int i;

	switch (c) {
	case IP_SOURCE:
		check_option(flags, OPT_SOURCE);
		ipinfo->bitmask |= EBT_IP_SOURCE;

		if (check_inverse(optarg))
			ipinfo->invflags |= EBT_IP_SOURCE;

		if (optind > argc)
			print_error("Missing IP address argument");
		parse_ip_address(argv[optind - 1], &ipinfo->saddr, &ipinfo->smsk);
		break;

	default:
		return 0;
	}
	return 1;
}
</PRE>
</td></tr>
</table>
</p>
<p>
This function parses the option, filling in the match specific struct or exiting on input errors. <CODE>check_option()</CODE> makes sure the option isn't
specified twice on the command line. <CODE>check_inverse()</CODE> checks if the argument equals '!'. <CODE>optind &gt; argc</CODE> is possible if the command
line input ends with '--ip-source !'.
</p>
<p>
<table BGCOLOR="#E0E0E0" WIDTH="100%">
<tr><td>
<PRE>
static void final_check(const struct ebt_u_entry *entry,
   const struct ebt_entry_match *match, const char *name,
   unsigned int hookmask, unsigned int time)
{
	if (entry->ethproto != ETH_P_IP || entry->invflags & EBT_IPROTO)
		print_error("For IP filtering the protocol must be "
		            "specified as IPv4");
}
</PRE>
</td></tr>
</table>
</p>
<p>
This function is executed for a rule that uses the (ip) module. When there is an error, it exits using <CODE>print_error()</CODE>.
<CODE>entry->ethproto</CODE> contains the protocol specified by the user. If the protocol wasn't specified, this field will equal zero. The specified protocol is
in host endian form, so there is no need for <CODE>ntohs()</CODE>.
</p>
<p>
<table BGCOLOR="#E0E0E0" WIDTH="100%">
<tr><td>
<PRE>
static void print(const struct ebt_u_entry *entry,
   const struct ebt_entry_match *match)
{
	struct ebt_ip_info *ipinfo = (struct ebt_ip_info *)match->data;
	int j;

	if (ipinfo->bitmask & EBT_IP_SOURCE) {
		printf("--ip-src ");
		if (ipinfo->invflags & EBT_IP_SOURCE)
			printf("! ");
		for (j = 0; j < 4; j++)
			printf("%d%s",((unsigned char *)&ipinfo->saddr)[j],
			   (j == 3) ? "" : ".");
		printf("%s ", mask_to_dotted(ipinfo->smsk));
	}
}
</PRE>
</td></tr>
</table>
</p>
<p>
Print out the data contained in the match in a form that the user could have used on the command line. End with at least one space.
</p>
<p>
<table BGCOLOR="#E0E0E0" WIDTH="100%">
<tr><td>
<PRE>
static int compare(const struct ebt_entry_match *m1,
   const struct ebt_entry_match *m2)
{
	struct ebt_ip_info *ipinfo1 = (struct ebt_ip_info *)m1->data;
	struct ebt_ip_info *ipinfo2 = (struct ebt_ip_info *)m2->data;

	if (ipinfo1->bitmask != ipinfo2->bitmask)
		return 0;
	if (ipinfo1->invflags != ipinfo2->invflags)
		return 0;
	if (ipinfo1->bitmask & EBT_IP_SOURCE) {
		if (ipinfo1->saddr != ipinfo2->saddr)
			return 0;
		if (ipinfo1->smsk != ipinfo2->smsk)
			return 0;
	}
	return 1;
}
</PRE>
</td></tr>
</table>
</p>
<p>
Compares the data of two "instances" of the match module, returning 1 if the data is equivalent, else 0.
</p>
<p>
<table BGCOLOR="#E0E0E0" WIDTH="100%">
<tr><td>
<PRE>
static struct ebt_u_match ip_match =
{
	.name		= EBT_IP_MATCH,
	.size		= sizeof(struct ebt_ip_info),
	.help		= print_help,
	.init		= init,
	.parse		= parse,
	.final_check	= final_check,
	.print		= print,
	.compare	= compare,
	.extra_ops	= opts,
};
</PRE>
</td></tr>
</table>
</p>
<p>
The struct used to register the match to the core ebtables userspace code.
</p>
<p>
<table BGCOLOR="#E0E0E0" WIDTH="100%">
<tr><td>
<PRE>
static void _init(void) __attribute((constructor));
static void _init(void)
{
	register_match(&ip_match);
}
</PRE>
</td></tr>
</table>
</p>
<p>
The init function registers the match. Initializing its own data should be done with the <CODE>init()</CODE> function defined int the above struct.
</p>
<p>
<em><h3>The (userspace) dnat <CODE>final_check()</CODE> function</h3></em>
</p>
<p>
<table BGCOLOR="#E0E0E0" WIDTH="100%">
<tr><td>
<PRE>
static void final_check_d(const struct ebt_u_entry *entry,
   const struct ebt_entry_target *target, const char *name,
   unsigned int hookmask, unsigned int time)
{
	struct ebt_nat_info *natinfo = (struct ebt_nat_info *)target->data;

	if (BASE_CHAIN && natinfo->target == EBT_RETURN)
		print_error("--dnat-target RETURN not allowed on base chain");
	CLEAR_BASE_CHAIN_BIT;
	if (((hookmask & ~((1 &lt;&lt; NF_BR_PRE_ROUTING) | (1 &lt;&lt; NF_BR_LOCAL_OUT)))
	   || strcmp(name, "nat")) &&
	   ((hookmask & ~(1 &lt;&lt; NF_BR_BROUTING)) || strcmp(name, "broute")))
		print_error("Wrong chain for dnat");
	if (time == 0 && to_dest_supplied == 0)
		print_error("No dnat address supplied");
}
</PRE>
</td></tr>
</table>
</p>
<p>
First we check that this target isn't RETURN on one of the standard (base) chains. Then we make
<CODE>hookmask</CODE> ready for direct use by using the <CODE>CLEAR_BASE_CHAIN_BIT</CODE> macro. Next is checked if the rule containing this
"module instance" is accessible through illegal chains or tables.
Finally, the argument <CODE>time</CODE> is checked. If it equals zero, the function checks to be sure a destination IP address was specified.
<CODE>to_dest_supplied</CODE> is a static variable of this ebtables userspace module, initialized to zero by its initialization function.
</p>
</h2>
<h2><a NAME="ss4.2">4.2</a> <a HREF="ebtables-hacking-HOWTO.html#toc4.2">Kernel</a>
</h2>
<p>
<em><h3>The ebtables kernel ip match module</h3></em>
</p>
<p>
What follows is annotated code of pieces of the <CODE>ebt_ip.c</CODE> code. For brevity, only the IP source address filtering is considered.
As the interface towards the main ebtables code is easy, there is really not much to be said here.
</p>
<p>
<table BGCOLOR="#E0E0E0" WIDTH="100%">
<tr><td>
<PRE>
#include &lt;linux/netfilter_bridge/ebtables.h&gt;
#include &lt;linux/netfilter_bridge/ebt_ip.h&gt;
#include &lt;linux/ip.h&gt;
#include &lt;linux/module.h&gt;
</PRE>
</td></tr>
</table>
</p>
<p>
We need general ebtables definitions, the ip kernel match module's specific data, the definition of the IP header and some module definitions.
</p>
<p>
<table BGCOLOR="#E0E0E0" WIDTH="100%">
<tr><td>
<PRE>
static int ebt_filter_ip(const struct sk_buff *skb, const struct net_device *in,
   const struct net_device *out, const void *data,
   unsigned int datalen)
{
	struct ebt_ip_info *info = (struct ebt_ip_info *)data;
	union {struct iphdr iph; struct tcpudphdr ports;} u;

	if (skb_copy_bits(skb, 0, &u.iph, sizeof(u.iph)))
		return EBT_NOMATCH;
	if (info->bitmask & EBT_IP_SOURCE &&
	   FWINV((u.iph.saddr & info->smsk) !=
	   info->saddr, EBT_IP_SOURCE))
		return EBT_NOMATCH;
}
</PRE>
</td></tr>
</table>
</p>
<p>
This is the filtering function of the ip match module, it is executed for every
frame that comes into contact with an ebtables rule that uses the ip match. All it does
is tell the ebtables main code if the frame matches or not.<br>
As the data isn't necessarily linearized in memory, meaning that the data isn't
guaranteed to be in consecutive memory places, we need to use skb_copy_bits()
to copy the IP header to the stack. We can then match the data on the stack.
</p>
<p>
<table BGCOLOR="#E0E0E0" WIDTH="100%">
<tr><td>
<PRE>
static int ebt_ip_check(const char *tablename, unsigned int hookmask,
   const struct ebt_entry *e, void *data, unsigned int datalen)
{
	struct ebt_ip_info *info = (struct ebt_ip_info *)data;

	if (datalen != sizeof(struct ebt_ip_info))
		return -EINVAL;
	if (e->ethproto != __constant_htons(ETH_P_IP) ||
	   e->invflags & EBT_IPROTO)
		return -EINVAL;
	if (info->bitmask & ~EBT_IP_MASK || info->invflags & ~EBT_IP_MASK)
		return -EINVAL;
	return 0;
}
</PRE>
</td></tr>
</table>
</p>
<p>
This function is executed for every rule that uses the ip match, when the kernel receives new table data. It needs to make sure no corrupt ip match data is accepted.
</p>
<p>
<table BGCOLOR="#E0E0E0" WIDTH="100%">
<tr><td>
<PRE>
static struct ebt_match filter_ip =
{
	.name		= EBT_IP_MATCH,
	.match		= ebt_filter_ip,
	.check		= ebt_ip_check,
	.me		= THIS_MODULE,
};
</PRE>
</td></tr>
</table>
</p>
<p>
The struct we'll give to the main ebtables code to register the match.
</p>
<p>
<table BGCOLOR="#E0E0E0" WIDTH="100%">
<tr><td>
<PRE>
static int __init init(void)
{
	return ebt_register_match(&filter_ip);
}

static void __exit fini(void)
{
	ebt_unregister_match(&filter_ip);
}

module_init(init);
module_exit(fini);
</PRE>
</td></tr>
</table>
</p>
<p>
Functions executed at loading or removing of the ip match module.
</p>
<p>
<table BGCOLOR="#E0E0E0" WIDTH="100%">
<tr><td>
<PRE>
MODULE_LICENSE("GPL");
</PRE>
</td></tr>
</table>
</p>
<p>
Of course your module is released under the GPL.
</p>
<p>
<em><h3>The ebtables kernel filter table</h3></em>
</p>
<p>
This part contains pieces of the <CODE>ebtable_filter.c</CODE> code with annotation.
</p>
<p>
<table BGCOLOR="#E0E0E0" WIDTH="100%">
<tr><td>
<PRE>
#define FILTER_VALID_HOOKS ((1 &lt;&lt; NF_BR_LOCAL_IN) | (1 &lt;&lt; NF_BR_FORWARD) | \
   (1 &lt;&lt; NF_BR_LOCAL_OUT))

</PRE>
</td></tr>
</table>
</p>
<p>
The valid netfilter hooks for the ebtables filter table are the bridge <CODE>LOCAL_IN</CODE> <CODE>FORWARD</CODE> and <CODE>LOCAL_OUT</CODE> hooks.
</p>
<p>
<table BGCOLOR="#E0E0E0" WIDTH="100%">
<tr><td>
<PRE>
static struct ebt_entries initial_chains[] =
{
	{
		.name	= "INPUT",
		.policy	= EBT_ACCEPT,
	},
	{
		.name	= "FORWARD",
		.policy	= EBT_ACCEPT,
	},
	{
		.name	= "OUTPUT",
		.policy	= EBT_ACCEPT,
	},
};

</PRE>
</td></tr>
</table>
</p>
<p>
The filter table consists of three chains, initially containing zero rules and having policy <CODE>ACCEPT</CODE>.
</p>
<p>
<table BGCOLOR="#E0E0E0" WIDTH="100%">
<tr><td>
<PRE>
static struct ebt_replace initial_table =
{
	.name		= "filter",
	.valid_hooks	= FILTER_VALID_HOOKS,
	.entries_size	= 3 * sizeof(struct ebt_entries),
	.hook_entry	= {
		[NF_BR_LOCAL_IN]	= &initial_chains[0],
		[NF_BR_FORWARD]		= &initial_chains[1],
		[NF_BR_LOCAL_OUT]	= &initial_chains[2],
	},
	.entries	= (char *)initial_chains,
};

</PRE>
</td></tr>
</table>
</p>
<p>
This contains all the info about the table.
</p>
<p>
<table BGCOLOR="#E0E0E0" WIDTH="100%">
<tr><td>
<PRE>
static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
{
	if (valid_hooks & ~FILTER_VALID_HOOKS)
		return -EINVAL;
	return 0;
}

</PRE>
</td></tr>
</table>
</p>
<p>
This function is executed when new table data is given to the kernel. We just check that
the valid hooks according to userspace are the same as those according to the kernel module.
</p>
<p>
<table BGCOLOR="#E0E0E0" WIDTH="100%">
<tr><td>
<PRE>
static struct ebt_table frame_filter =
{ 
	.name		= "filter",
	.table		= &initial_table,
	.valid_hooks	= FILTER_VALID_HOOKS, 
	.lock		= RW_LOCK_UNLOCKED,
	.check		= check,
	.me		= THIS_MODULE,
};

</PRE>
</td></tr>
</table>
</p>
<p>
The ebtables main code likes to use this struct.
</p>
<p>
<table BGCOLOR="#E0E0E0" WIDTH="100%">
<tr><td>
<PRE>
static unsigned int
ebt_hook (unsigned int hook, struct sk_buff **pskb, const struct net_device *in,
   const struct net_device *out, int (*okfn)(struct sk_buff *))
{
	return ebt_do_table(hook, pskb, in, out, &frame_filter);
}

</PRE>
</td></tr>
</table>
</p>
<p>
This function is executed for every frame that passes through a netfilter hook on which this function is registered.
</p>
<p>
<table BGCOLOR="#E0E0E0" WIDTH="100%">
<tr><td>
<PRE>
static struct nf_hook_ops ebt_ops_filter[] = {
	{
		.hook		= ebt_hook,
		.owner		= THIS_MODULE,
		.pf		= PF_BRIDGE,
		.hooknum	= NF_BR_LOCAL_IN,
		.priority	= NF_BR_PRI_FILTER_BRIDGED,
	},
	{
		.hook		= ebt_hook,
		.owner		= THIS_MODULE,
		.pf		= PF_BRIDGE,
		.hooknum	= NF_BR_FORWARD,
		.priority	= NF_BR_PRI_FILTER_BRIDGED,
	},
	{
		.hook		= ebt_hook,
		.owner		= THIS_MODULE,
		.pf		= PF_BRIDGE,
		.hooknum	= NF_BR_LOCAL_OUT,
		.priority	= NF_BR_PRI_FILTER_OTHER,
	},
};

</PRE>
</td></tr>
</table>
</p>
<p>
Here we say the function <CODE>ebt_hook()</CODE> is registered onto the three mentioned netfilter hooks, with a certain priority (e.g. NF_BR_PRI_FILTER_BRIDGED).
If multiple functions are registered on the same hook, the priority of the functions determines the order in which they are executed. The lower the priority value,
the earlier the function will get executed.
</p>
<p>
<table BGCOLOR="#E0E0E0" WIDTH="100%">
<tr><td>
<PRE>
static int __init init(void)
{
	int i, j, ret;

	ret = ebt_register_table(&frame_filter);
	if (ret &lt; 0)
		return ret;
	for (i = 0; i &lt; ARRAY_SIZE(ebt_ops_filter); i++)
		if ((ret = nf_register_hook(&ebt_ops_filter[i])) &lt; 0)
			goto cleanup;
	return ret;
cleanup:
	for (j = 0; j &lt; i; j++)
		nf_unregister_hook(&ebt_ops_filter[j]);
	ebt_unregister_table(&frame_filter);
	return ret;
}

</PRE>
</td></tr>
</table>
</p>
<p>
Register the table to the main ebtables code; register <CODE>ebt_hook()</CODE> on the appropriate netfilter hooks.
</p>
<p>
<table BGCOLOR="#E0E0E0" WIDTH="100%">
<tr><td>
<PRE>
static void __exit fini(void)
{
	int i;

	for (i = 0; i &lt; ARRAY_SIZE(ebt_ops_filter); i++)
		nf_unregister_hook(&ebt_ops_filter[i]);
	ebt_unregister_table(&frame_filter);
}
</PRE>
</td></tr>
</table>
</p>
<p>
Unregister from the netfilter hooks and ebtables.
</p>
<hr>
Next
<a HREF="ebtables-hacking-HOWTO-3.html">Previous</a>
<a HREF="ebtables-hacking-HOWTO.html#toc4">Contents</a>
</body>
</html>
